package com.meego.common.crypto;


import com.meego.common.crypto.KeyStore.KeyStoreType;
import com.meego.common.log.GA;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.EncodedKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class RSA {

    private PrivateKey prk;
    private PublicKey plk;
    private static RSA cryptoRSA;

    public static RSA getInstance() {
        if (cryptoRSA == null) {
            createInstance();
        }
        return cryptoRSA;
    }

    private static synchronized void createInstance() {
        if (cryptoRSA == null) {
            cryptoRSA = new RSA();
        }
    }

    protected RSA() {
        RSAKeyParameters prik = CryptoKey.getInstance().getRsaPrivateKey(KeyStoreType.STK, 0);
        RSAKeyParameters pubk = CryptoKey.getInstance().getRsaPublicKey(KeyStoreType.STK, 0);
        if (prik != null && pubk != null) {
            try {
                PublicKey pub = CryptoKey.getInstance().getRsaPublicKey(pubk);
                PrivateKey pri = CryptoKey.getInstance().getRsaPrivatKey(prik);
                loadCryptoRSA(pub, pri);
            } catch (Exception e) {
                e.printStackTrace();
                GA.crypto.error("CryptoRSA: Can not load RSA key", e);
            }
        }
    }

    /**
     * Load default public key and private keys. These keys are used to
     * encrypt/decrypt, sign/verify data.
     *
     * @param pub The string contain the public key
     * @param pri The string contain the private key
     * @throws Exception
     */
    public void loadCryptoRSA(String pub, String pri) throws Exception {
        PrivateKey prik;
        PublicKey pubk;
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(pri.getBytes());
            prik = keyFactory.generatePrivate(privateKeySpec);

            EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pub.getBytes());
            pubk = keyFactory.generatePublic(publicKeySpec);
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not load RSA key", e);
            throw e;
        }
        try {
            String enc = base64Encrypt("test encrypt", pubk);
            base64Decrypt(enc, prik);
            this.plk = pubk;
            this.prk = prik;
        } catch (Exception e) {
            CryptException t = new CryptException("RSA Key pairs not match", "RSA");
            GA.crypto.error("CryptoRSA: Can not load RSA key", t);
            throw t;
        }
    }

    public void loadCryptoRSA(byte[] pub, byte[] pri) throws Exception {
        PrivateKey prik;
        PublicKey pubk;
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(pri);
            prik = keyFactory.generatePrivate(privateKeySpec);

            EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pub);
            pubk = keyFactory.generatePublic(publicKeySpec);
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not load RSA key", e);
            throw e;
        }
        try {
            String enc = base64Encrypt("test encrypt", pubk);
            base64Decrypt(enc, prik);
            this.plk = pubk;
            this.prk = prik;
        } catch (Exception e) {
            CryptException t = new CryptException("RSA Key pairs not match", "RSA");
            GA.crypto.error("CryptoRSA: Can not load RSA key", t);
            throw t;
        }
    }

    public void loadBase64CryptoRSA(String pub, String pri) throws Exception {
        PrivateKey prikey;
        PublicKey pubkey;
        try {
            BASE64Decoder decoder = new BASE64Decoder();
            byte[] prik = decoder.decodeBuffer(pri);
            byte[] pubk = decoder.decodeBuffer(pub);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(prik);
            prikey = keyFactory.generatePrivate(privateKeySpec);

            EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pubk);
            pubkey = keyFactory.generatePublic(publicKeySpec);
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not load RSA key", e);
            throw e;
        }
        try {
            String enc = base64Encrypt("test encrypt", pubkey);
            base64Decrypt(enc, prikey);
            this.plk = pubkey;
            this.prk = prikey;
        } catch (Exception e) {
            CryptException t = new CryptException("RSA Key pairs not match", "RSA");
            GA.crypto.error("CryptoRSA: Can not load RSA key", t);
            throw t;
        }
    }

    private void loadCryptoRSA(PublicKey pub, PrivateKey pri) throws Exception {
        try {
            String enc = base64Encrypt("test encrypt", pub);
            base64Decrypt(enc, pri);
        } catch (Exception e) {
            e.printStackTrace();
            CryptException t = new CryptException("RSA Key pairs not match", "RSA");
            GA.crypto.error("CryptoRSA key pair not match", t);
            throw t;
        }
        prk = pri;
        plk = pub;
    }

    public String decrypt(String enc, PrivateKey prk) throws Exception {
        String result = "";
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, prk);
            result = new String(cipher.doFinal(enc.getBytes()));
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not decrypt data", e);
            throw e;
        }
        return result;
    }

    public byte[] decrypt(byte[] enc, PrivateKey prk) throws Exception {
        byte[] result;
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, prk);
            result = cipher.doFinal(enc);
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not decrypt data", e);
            throw e;
        }
        return result;
    }

    public String base64Decrypt(String enc, PrivateKey prk) throws Exception {
        String result = "";
        try {
            BASE64Decoder decoder = new BASE64Decoder();
            byte[] data = decoder.decodeBuffer(enc);
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, prk);
            result = new String(cipher.doFinal(data));
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not decrypt data", e);
            throw e;
        }
        return result;
    }

    public String decrypt(String enc) throws Exception {
        if (cryptoRSA == null) {
            CryptException t = new CryptException("instance not yet created", "RSA");
            GA.config.error("CryptoRSA not yet loaded", t);
            throw t;
        }
        if (prk == null) {
            CryptException t = new CryptException("Key not found exception", "RSA");
            GA.config.error("dml.crypto.rsa.key.prk", t);
            throw t;
        }
        String result = "";
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, prk);
            result = new String(cipher.doFinal(enc.getBytes()));
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not decrypt data", e);
            throw e;
        }
        return result;
    }

    public byte[] decrypt(byte[] enc) throws Exception {
        if (cryptoRSA == null) {
            CryptException t = new CryptException("instance not yet created", "RSA");
            GA.config.error("CryptoRSA not yet loaded", t);
            throw t;
        }
        if (prk == null) {
            CryptException t = new CryptException("Key not found exception", "RSA");
            GA.config.error("CryptoRSA Private key not yet set", t);
            throw t;
        }
        byte[] result;
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, prk);
            result = cipher.doFinal(enc);
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not decrypt data", e);
            throw e;
        }
        return result;
    }

    public String base64Decrypt(String enc) throws Exception {
        if (cryptoRSA == null) {
            CryptException t = new CryptException("instance not yet created", "RSA");
            GA.config.error("CryptoRSA not yet loaded", t);
            throw t;
        }
        if (prk == null) {
            CryptException t = new CryptException("Key not found exception", "RSA");
            GA.config.error("CryptoRSA Private key not yet set", t);
            throw t;
        }
        String result = "";
        try {
            BASE64Decoder decoder = new BASE64Decoder();
            byte[] data = decoder.decodeBuffer(enc);
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, prk);
            result = new String(cipher.doFinal(data));
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not decrypt data", e);
            throw e;
        }
        return result;
    }

    public byte[] encrypt(byte[] data, PublicKey plk) throws Exception {
        byte[] result = {0};
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, plk);
            result = cipher.doFinal(data);
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not encrypt data", e);
            throw e;
        }
        return result;
    }

    public String encrypt(String data, PublicKey plk) throws Exception {
        String result = "";
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, plk);
            result = new String(cipher.doFinal(data.getBytes()));
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not encrypt data", e);
            throw e;
        }

        return result;
    }

    public String base64Encrypt(String data, PublicKey plk) throws Exception {
        String result = "";
        BASE64Encoder encoder = new BASE64Encoder();
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, plk);
            result = encoder.encode(cipher.doFinal(data.getBytes())).replace("\r", "");
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not encrypt data", e);
            throw e;
        }

        return result;
    }

    public byte[] encrypt(byte[] data) throws Exception {
        if (cryptoRSA == null) {
            CryptException t = new CryptException("instance not yet created", "RSA");
            GA.config.error("CryptoRSA not yet loaded", t);
            throw t;
        }
        if (plk == null) {
            CryptException t = new CryptException("Key not found exception", "RSA");
            GA.config.error("CryptoRSA Public key not yet set", t);
            throw t;
        }
        byte[] result = {0};
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, plk);
            result = cipher.doFinal(data);
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not encrypt data", e);
            throw e;
        }
        return result;
    }

    public String encrypt(String data) throws Exception {
        if (cryptoRSA == null) {
            CryptException t = new CryptException("instance not yet created", "RSA");
            GA.config.error("CryptoRSA not yet loaded", t);
            throw t;
        }
        if (plk == null) {
            CryptException t = new CryptException("Key not found exception", "RSA");
            GA.config.error("CryptoRSA Public key not yet set", t);
            throw t;
        }
        String result = "";
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, plk);
            result = new String(cipher.doFinal(data.getBytes()));
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not encrypt data", e);
            throw e;
        }

        return result;
    }

    public String base64Encrypt(String data) throws Exception {
        if (cryptoRSA == null) {
            CryptException t = new CryptException("instance not yet created", "RSA");
            GA.config.error("CryptoRSA not yet loaded", t);
            throw t;
        }
        if (plk == null) {
            CryptException t = new CryptException("Key not found exception", "RSA");
            GA.config.error("CryptoRSA Public key not yet set", t);
            throw t;
        }
        String result = "";
        BASE64Encoder encoder = new BASE64Encoder();
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, plk);
            result = encoder.encode(cipher.doFinal(data.getBytes())).replace("\r", "");
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not encrypt data", e);
            throw e;
        }

        return result;
    }

    public byte[] sign(byte[] data, PrivateKey prk) throws Exception {
        try {
            Signature rsa = Signature.getInstance("SHA1withRSA");
            rsa.initSign(prk);
            rsa.update(data);
            return rsa.sign();
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not sign data", e);
            throw e;
        }
    }

    public String base64Sign(String data, PrivateKey prk) throws Exception {
        try {
            BASE64Encoder encoder = new BASE64Encoder();
            Signature rsa = Signature.getInstance("SHA1withRSA");
            rsa.initSign(prk);
            rsa.update(data.getBytes());
            return encoder.encode(rsa.sign()).replace("\r", "");
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not sign data", e);
            throw e;
        }
    }

    public byte[] sign(byte[] data) throws Exception {
        if (cryptoRSA == null) {
            CryptException t = new CryptException("instance not yet created", "RSA");
            GA.config.error("CryptoRSA not yet loaded", t);
            throw t;
        }
        if (prk == null) {
            CryptException t = new CryptException("Key not found exception", "RSA");
            GA.config.error("CryptoRSA Private key not yet set", t);
            throw t;
        }
        try {
            Signature rsa = Signature.getInstance("SHA1withRSA");
            rsa.initSign(prk);
            rsa.update(data);
            return rsa.sign();
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not sign data", e);
            throw e;
        }
    }

    public String base64Sign(String data) throws Exception {
        if (cryptoRSA == null) {
            CryptException t = new CryptException("instance not yet created", "RSA");
            GA.config.error("CryptoRSA not yet loaded", t);
            throw t;
        }
        if (prk == null) {
            CryptException t = new CryptException("Key not found exception", "RSA");
            GA.config.error("CryptoRSA Private key not yet set", t);
            throw t;
        }
        try {
            BASE64Encoder encoder = new BASE64Encoder();
            Signature rsa = Signature.getInstance("SHA1withRSA");
            rsa.initSign(prk);
            rsa.update(data.getBytes());
            return encoder.encode(rsa.sign()).replace("\r", "");
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not sign data", e);
            throw e;
        }
    }

    public boolean verify(byte[] signature, byte[] data, PublicKey plk) throws Exception {
        try {
            Signature sign = Signature.getInstance("SHA1withRSA");
            sign.initVerify(plk);
            sign.update(data);
            return sign.verify(signature);
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not verify data", e);
            throw e;
        }
    }

    public boolean base64Verify(String signature, String data, PublicKey plk) throws Exception {
        try {
            BASE64Decoder decoder = new BASE64Decoder();

            Signature sign = Signature.getInstance("SHA1withRSA");
            byte[] ssignature = decoder.decodeBuffer(signature);
            sign.initVerify(plk);
            sign.update(data.getBytes());
            return sign.verify(ssignature);
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not verify data", e);
            throw e;
        }
    }

    public boolean verify(byte[] signature, byte[] data) throws Exception {
        if (cryptoRSA == null) {
            CryptException t = new CryptException("instance not yet created", "RSA");
            GA.config.error("CryptoRSA not yet loaded", t);
            throw t;
        }
        if (plk == null) {
            CryptException t = new CryptException("Key not found exception", "RSA");
            GA.config.error("CryptoRSA Public key not yet set", t);
            throw t;
        }
        try {
            Signature sign = Signature.getInstance("SHA1withRSA");
            sign.initVerify(plk);
            sign.update(data);
            return sign.verify(signature);
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not verify data", e);
            throw e;
        }
    }

    public boolean base64Verify(String signature, String data) throws Exception {
        if (cryptoRSA == null) {
            CryptException t = new CryptException("instance not yet created", "RSA");
            GA.config.error("CryptoRSA not yet loaded", t);
            throw t;
        }
        if (plk == null) {
            CryptException t = new CryptException("Key not found exception", "RSA");
            GA.config.error("CryptoRSA Public key not yet set", t);
            throw t;
        }
        try {
            BASE64Decoder decoder = new BASE64Decoder();

            Signature sign = Signature.getInstance("SHA1withRSA");
            byte[] ssignature = decoder.decodeBuffer(signature);
            sign.initVerify(plk);
            sign.update(data.getBytes());
            return sign.verify(ssignature);
        } catch (Exception e) {
            GA.crypto.error("CryptoRSA: Can not verify data", e);
            throw e;
        }
    }
}
